#include "drawable.h"
#include "scene.h"
#include <osg/ComputeBoundsVisitor>
#include <osg/Node>
#include <osg/Switch>
#include <assert.h>
#include "actorbase.h"

namespace CoreBase
{
	class DrawablePrivate
	{
	public:
		DrawablePrivate();
		~DrawablePrivate();
		void setParent(Drawable* parent);
		Drawable* getParent();
		const Drawable* getParent() const;
		void addedToScene(Scene* scene, osg::Node* node);
		void removedFromScene(Scene* scene, osg::Node* node);
		Scene* getSceneParent();
		const Scene* getSceneParent() const;
		bool addChild(Drawable* child, Drawable* parent);
		void removeChild(Drawable* child);
		unsigned int getNumChildren() const;
		Drawable* getChild(unsigned int idx);
		const Drawable* getChild(unsigned int idx) const;
		unsigned int getChildIndex(const Drawable* child) const;
		bool canBeChild(Drawable* child) const;
		void getBoundingSphere(osg::Vec3& center, float& radius, osg::Node* node);
		void setActive(bool enable, osg::Node* node);
		bool getActive() const;
		void setDescription(const QString& description);
		const QString& getDescription() const;
		void OnOrphaned();
	private:
		void insertSwitchNode(osg::Node *node);
		void removeSwitchNode(osg::Node* node);
		Drawable* _parent;
		typedef QVector<osg::ref_ptr<Drawable> > ChildList;
		ChildList _childList;
		Scene* _parentScene;
		bool _isActive;
		QString _description;
	};

	void DrawablePrivate::setParent(Drawable* parent)
	{
		_parent = parent;
	}

	Drawable* DrawablePrivate::getParent()
	{
		return _parent;
	}

	const Drawable* DrawablePrivate::getParent() const
	{
		return _parent;
	}

	void DrawablePrivate::OnOrphaned()
	{
		_parent = NULL;
	}

	Scene* DrawablePrivate::getSceneParent()
	{
		return _parentScene;
	}

	const Scene* DrawablePrivate::getSceneParent() const
	{
		return _parentScene;
	}

	void DrawablePrivate::removeChild(Drawable* child)
	{
		if (!child) return;
		unsigned int pos = getChildIndex(child);
		if (pos < _childList.size())
		{
			child->setParent(NULL);
			if (getSceneParent() != NULL)
			{
				child->addedToScene(NULL);
				child->removedFromScene(getSceneParent());
			}
			_childList.erase(_childList.begin() + pos);
			child = NULL;
		}
	}

	unsigned int DrawablePrivate::getNumChildren() const
	{
		return _childList.size();
	}

	Drawable* DrawablePrivate::getChild(unsigned int idx)
	{
		if (idx >= getNumChildren())
		{
			return NULL;
		}
		return _childList[idx].get();
	}

	const Drawable* DrawablePrivate::getChild(unsigned int idx) const
	{
		if (idx >= getNumChildren())
		{
			return NULL;
		}
		return _childList[idx].get();
	}

	unsigned int DrawablePrivate::getChildIndex(const Drawable* child) const
	{
		for (unsigned int childNum = 0; childNum < _childList.size(); ++childNum)
		{
			if (_childList[childNum] == child)
			{
				return childNum;
			}
		}
		return -1;
	}

	void DrawablePrivate::getBoundingSphere(osg::Vec3& center, float& radius, osg::Node* node)
	{
		if (node != NULL)
		{
			osg::BoundingSphere bs = node->getBound();
			center.set(bs.center());
			radius = bs.radius();
		}
		else
		{
			//LOG_WARNING("Can't calculate Bounding Sphere, there is no geometry associated with this Drawable");
		}
	}
	void DrawablePrivate::setActive(bool enable, osg::Node* node)
	{
		if (_isActive == enable)
		{
			return;
		}
		_isActive = enable;
		if (_parentScene == NULL)
		{
			return;
		}
		if (_isActive == false)
		{
			insertSwitchNode(node);
		}
		else
		{
			removeSwitchNode(node);
		}
	}

	bool DrawablePrivate::getActive() const
	{
		return _isActive;
	}

	void DrawablePrivate::insertSwitchNode(osg::Node *node)
	{
		osg::Node::ParentList parents = node->getParents();
		osg::Node::ParentList::iterator parentItr = parents.begin();
		while (parentItr != parents.end())
		{
			(*parentItr)->removeChild(node);
			++parentItr;
		}
		osg::ref_ptr<osg::Switch> parentSwitch = new osg::Switch();
		parentSwitch->setAllChildrenOff();
		parentSwitch->addChild(node);
		parentItr = parents.begin();
		while (parentItr != parents.end())
		{
			(*parentItr)->addChild(parentSwitch.get());
			++parentItr;
		}
	}

	void DrawablePrivate::removeSwitchNode(osg::Node* node)
	{
		if (node == NULL) { return; }  // no geometry?
		if (node->getNumParents() == 0) { return; }  // no parents?
		osg::Switch* parentSwitch = dynamic_cast<osg::Switch*>(node->getParent(0));
		if (parentSwitch == NULL) { return; }
		osg::Node::ParentList parents = parentSwitch->getParents();
		// remove the Switch node from all its parents
		osg::Node::ParentList::iterator parentItr = parents.begin();
		while (parentItr != parents.end())
		{
			(*parentItr)->removeChild(parentSwitch);
			++parentItr;
		}
		// Add the Node as a child to what was the Switch node's parents
		parentItr = parents.begin();
		while (parentItr != parents.end())
		{
			(*parentItr)->addChild(node);
			++parentItr;
		}
	}

	void DrawablePrivate::setDescription(const QString& description)
	{
		_description = description;
	}

	const QString& DrawablePrivate::getDescription() const
	{
		return _description;
	}

	void DrawablePrivate::addedToScene(Scene* scene, osg::Node* node)
	{
		if (_parentScene == scene) { return; } // nothing to do here.
		_parentScene = scene;
		for (ChildList::iterator itr = _childList.begin();
			itr != _childList.end();
			++itr)
		{
			(*itr)->addedToScene(scene);
		}
		// If we've been set to inactive before being added to a Scene,
		// then we need to do add in our Switch node.
		if (_parentScene != NULL)
		{
			if (getActive() == false)
			{
				insertSwitchNode(node);
			}
		}
	}
	void DrawablePrivate::removedFromScene(Scene* scene, osg::Node* node)
	{
		_parentScene = NULL;
		for (ChildList::iterator itr = _childList.begin();
			itr != _childList.end();
			++itr)
		{
			(*itr)->removedFromScene(scene);
		}
		// we've just been removed from a scene
		// if we're inactive, we should remove our Switch
		if (getActive() == false)
		{
			removeSwitchNode(node);
		}
	}
	bool DrawablePrivate::addChild(Drawable* child, Drawable* parent)
	{
		if (!canBeChild(child))
		{
			return false;
		}
		child->setParent(parent);
		_childList.push_back(child);

		if (_parentScene)
		{
			child->addedToScene(_parentScene);
		}
		return true;
	}
	bool DrawablePrivate::canBeChild(Drawable* child) const
	{
		if (child->getParent() != NULL)
		{
			return false;
		}
		//loop through parent's parents and make sure they're not == child
		osg::ref_ptr<const Drawable> t = getParent();
		while (t != NULL)
		{
			if (t == child)
			{
				return false;
			}
			t = t->getParent();
		}
		return true;
	}

	DrawablePrivate::DrawablePrivate():_parent(NULL), _parentScene(NULL), _isActive(true)
	{}

	DrawablePrivate::~DrawablePrivate()
	{}

	Drawable::Drawable(ActorBase* _ownerActor,const QString& name): Base(name)
		, _pvt(new DrawablePrivate()),_ownerActor(_ownerActor)
	{}

	Drawable::~Drawable()
	{
		for (unsigned int i = 0; i < _pvt->getNumChildren(); ++i)
		{
			Drawable* child = _pvt->getChild(i);
			child->OnOrphaned();
		}
		delete _pvt;
	}

	bool Drawable::addChild(Drawable* child)
	{
		return _pvt->addChild(child, this);
	}

	Drawable* Drawable::getChild(unsigned int idx)
	{
		return _pvt->getChild(idx);
	}

	const Drawable* Drawable::getChild(unsigned int idx) const
	{
		return _pvt->getChild(idx);
	}

	void Drawable::removeChild(Drawable* child)
	{
		if (child->getParent() != this && child->getParent() != NULL)
		{
			return;
		}
		return _pvt->removeChild(child);
	}

	unsigned int Drawable::getChildIndex(const Drawable* child) const
	{
		return _pvt->getChildIndex(child);
	}

	bool Drawable::canBeChild(Drawable* child) const
	{
		if (this == child)
		{
			return false;
		}
		return _pvt->canBeChild(child);
	}

	void Drawable::addedToScene(Scene* scene)
	{
		_pvt->addedToScene(scene, getOSGNode());
	}

	void Drawable::removedFromScene(Scene* scene)
	{
		assert(scene);
		_pvt->removedFromScene(scene, getOSGNode());
	}

	void Drawable::OnOrphaned()
	{
		_pvt->OnOrphaned();
	}

	void Drawable::getBoundingSphere(osg::Vec3& center, float& radius)
	{
		_pvt->getBoundingSphere(center, radius, getOSGNode());
	}

	osg::BoundingBox Drawable::getBoundingBox()
	{
		osg::Node* topNode = this->getOSGNode();      
		if (topNode == NULL)
		{
			osg::BoundingBox bb;
			return bb;
		}
		osg::ComputeBoundsVisitor cbv;
		topNode->accept(cbv);
		return cbv.getBoundingBox();   
	}

	void Drawable::setActive(bool enable)
	{
		_pvt->setActive(enable, getOSGNode());
	}

	bool Drawable::getActive() const
	{
		return _pvt->getActive();
	}

	void Drawable::setParent(Drawable* parent)
	{
		_pvt->setParent(parent);
	}

	Drawable* Drawable::getParent()
	{
		return _pvt->getParent();
	}

	const Drawable* Drawable::getParent() const
	{
		return _pvt->getParent();
	}

	Scene* Drawable::getSceneParent()
	{
		return _pvt->getSceneParent();
	}

	const Scene* Drawable::getSceneParent() const
	{
		return _pvt->getSceneParent();
	}

	unsigned int Drawable::getNumChildren() const
	{
		return _pvt->getNumChildren();
	}

	void Drawable::setDescription(const QString& description)
	{
		_pvt->setDescription(description);
	}

	const QString& Drawable::getDescription() const
	{
		return _pvt->getDescription();
	}
}